package cn.tedu.mall.seckill.service.impl;

import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.pojo.domain.CsmallAuthenticationInfo;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.order.service.IOmsOrderService;
import cn.tedu.mall.pojo.order.dto.OrderAddDTO;
import cn.tedu.mall.pojo.order.dto.OrderItemAddDTO;
import cn.tedu.mall.pojo.order.vo.OrderAddVO;
import cn.tedu.mall.pojo.seckill.dto.SeckillOrderAddDTO;
import cn.tedu.mall.pojo.seckill.dto.SeckillOrderItemAddDTO;
import cn.tedu.mall.pojo.seckill.model.SeckillSuccess;
import cn.tedu.mall.pojo.seckill.vo.SeckillCommitVO;
import cn.tedu.mall.seckill.config.RabbitMqComponentConfiguration;
import cn.tedu.mall.seckill.service.ISeckillService;
import cn.tedu.mall.seckill.utils.SeckillCacheUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
@Slf4j
public class SeckillServiceImpl implements ISeckillService {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @DubboReference
    private IOmsOrderService dubboOrderService;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    /*
      1.判断用户是否重复购买
      2.判断库存是否充足
      3.秒杀订单转成普通订单
      4.秒杀成功的信息暂存到消息队列中
     */

    @Override
    public SeckillCommitVO commitSeckill(SeckillOrderAddDTO seckillOrderAddDTO) {
        //第一部分：利用Redis进行重复购买的判断和库存是否充足的判断
        //判断用户是否重复购买，利用skuId+userId
        Long userId = getUserId();
        Long skuId = seckillOrderAddDTO.getSeckillOrderItemAddDTO().getSkuId();
        //获取到userId+skuId的key
        String reseckillCheckKey = SeckillCacheUtils.getReseckillCheckKey(skuId, userId);
        //使用redis中的increment(),调用该方法时，如果不存在，就是设置成1，如果存在，在原有值基础上默认+1。并且会把值返回给用户
        Long increment = stringRedisTemplate.boundValueOps(reseckillCheckKey).increment();
        //只要increment返回值不大于1，就不是重复购买
        if(increment>1)
            //如果大于1，就是重复购买，终止程序运行
            throw new CoolSharkServiceException(ResponseCode.FORBIDDEN,"您已经购买过该商品");
        //程序到此出，就证明，用户是第一次购买
        //判断库存是否充足
        //使用Redis中的decremant()。调用一次，默认-1，并把减后的值返回给调用者
        //库存对应的key
        String stockKey = SeckillCacheUtils.getStockKey(skuId);
        Long decrement = stringRedisTemplate.boundValueOps(stockKey).decrement();
        //只要decrement不小于0，就说明库存充足
        if(decrement<0){
            //库存不充足，删除购买记录
            stringRedisTemplate.boundValueOps(reseckillCheckKey).decrement();
            //提示库存不足
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"对不起，您购买的商品已售罄");
        }
        //第二部分：将秒杀订单转成普通订单
        OrderAddDTO orderAddDTO = covertSeckillOrderToOrder(seckillOrderAddDTO);
        //给userId属性进行赋值
        orderAddDTO.setUserId(userId);
        //普通订单需要的订单信息准备完毕，生成普通订单
        OrderAddVO orderAddVO = dubboOrderService.addOrder(orderAddDTO);
        //第三部分：将秒杀成功的信息，放到RabbitMQ中
        SeckillSuccess seckillSuccess = new SeckillSuccess();
        seckillSuccess.setUserId(userId);
        seckillSuccess.setUserPhone(orderAddDTO.getMobilePhone());
        seckillSuccess.setSkuId(skuId);
        seckillSuccess.setQuantity(seckillOrderAddDTO.getSeckillOrderItemAddDTO().getQuantity());
        seckillSuccess.setSeckillPrice(seckillOrderAddDTO.getSeckillOrderItemAddDTO().getPrice());
        seckillSuccess.setOrderSn(orderAddVO.getSn());
        rabbitTemplate.convertAndSend(RabbitMqComponentConfiguration.SECKILL_EX,
                RabbitMqComponentConfiguration.SECKILL_RK,seckillSuccess);
        //声明当前方法的返回值类型
        SeckillCommitVO seckillCommitVO = new SeckillCommitVO();
        BeanUtils.copyProperties(orderAddVO,seckillCommitVO);
        //返回
        return seckillCommitVO;
    }
    //将seckillOrderAddDTO转成OrderAddDTO
    private OrderAddDTO covertSeckillOrderToOrder(SeckillOrderAddDTO seckillOrderAddDTO) {
        //声明orderAddDTO对象
        OrderAddDTO orderAddDTO = new OrderAddDTO();
        //同名属性赋值
        BeanUtils.copyProperties(seckillOrderAddDTO,orderAddDTO);
        //orderAddDTO中的订单项是List<OrderItemAddDTO>
        //seckillOrderAddDTO中的订单项是SeckillOrderItemAddDTO
        //进行订单项的转化
        List<OrderItemAddDTO> orderItemAddDTOList = new ArrayList<>();
        OrderItemAddDTO orderItemAddDTO = new OrderItemAddDTO();
        SeckillOrderItemAddDTO seckillOrderItemAddDTO = seckillOrderAddDTO.getSeckillOrderItemAddDTO();
        //进行同名属性赋值
        BeanUtils.copyProperties(seckillOrderItemAddDTO,orderItemAddDTO);
//        orderItemAddDTO.setSkuId(seckillOrderItemAddDTO.getSkuId());
//        orderItemAddDTO.setQuantity(seckillOrderItemAddDTO.getQuantity());
        //添加到集合中
        orderItemAddDTOList.add(orderItemAddDTO);
        //赋值到orderAddDTO对象中
        orderAddDTO.setOrderItems(orderItemAddDTOList);
        //记得返回
        return orderAddDTO;
    }

    //获取用户信息的方法
    public CsmallAuthenticationInfo getUserInfo(){
        //通过security上下文对象获取token
        UsernamePasswordAuthenticationToken token =
                (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        //判断token是否为null
        if (token==null)
            throw new CoolSharkServiceException(ResponseCode.UNAUTHORIZED,"用户没有登录");

        CsmallAuthenticationInfo userInfo = (CsmallAuthenticationInfo) token.getCredentials();
        //返回用户信息
        return userInfo;
    }
    //获取用户id
    public Long getUserId(){
        return getUserInfo().getId();
    }
}
